#!/usr/bin/env python3
#
# Copyright (c) 2025 Valve Corporation
# Copyright (c) 2025 LunarG, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Take the JSON with our settings and generates the other setting related files
# See docs/settings.md

import common_ci
import json
import os

def IsDebugSetting(setting):
    if setting['key'] == 'debug_action':
        return False
    return 'debug' in setting['key']

def GenerateTextFile(all_settings):
    out = []
    out.append(f'''# The settings in this file can be used to configure the behavior of layers in this repository.
# This file doesn't display the dependencies on the various settings
#
# This file is generated by {os.path.basename(__file__)}

# VK_LAYER_KHRONOS_validation
               ''')

    for setting in all_settings:
        if IsDebugSetting(setting):
            continue
        if 'view' in setting and setting['view'] == 'HIDDEN':
            continue

        default = setting['default']
        type = setting['type']
        if type == 'BOOL':
            default = 'true' if setting['default'] is True else 'false'
        if type == 'FLAGS' or type == 'LIST':
            default = ','.join(default)

        out.append(f'# {setting["label"]}')
        out.append('# =====================')
        out.append(f'# {setting["description"]}')
        out.append(f'khronos_validation.{setting["key"]} = {default}\n')

    text_output = common_ci.RepoRelative('layers/vk_layer_settings.txt')
    with open(text_output, 'w') as file:
        file.write('\n'.join(out))

def GenerateValidation(all_settings):
    out = []
    out.append(f'''// *** THIS FILE IS GENERATED - DO NOT EDIT ***
// See {os.path.basename(__file__)} for modifications

/***************************************************************************
 *
 * Copyright (c) 2025 Valve Corporation
 * Copyright (c) 2025 LunarG, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */''')

    out.append('''
#pragma once
// clang-format off

// This function is generated from scripts/generate_settings.py
static void ValidateLayerSettingsProvided(const VkLayerSettingsCreateInfoEXT &layer_setting_create_info,
                                          std::vector<std::string> &setting_warnings) {
    // Found that a set of <const char*> doesn't detect duplicates on all compilers
    vvl::unordered_set<std::string> used_settings;

    for (uint32_t i = 0; i < layer_setting_create_info.settingCount; i++) {
        const VkLayerSettingEXT &setting = layer_setting_create_info.pSettings[i];
        if (strcmp(OBJECT_LAYER_NAME, setting.pLayerName) != 0) {
            continue;
        }

        // used as a backup for settings not listed below
        VkLayerSettingTypeEXT required_type = VK_LAYER_SETTING_TYPE_MAX_ENUM_EXT;

        // Debugging settings are not added here as those are for internal development
        // and not designed for an app to use via VkLayerSettings API
        const char* name = setting.pSettingName;
        if (strcmp(VK_LAYER_ENABLES, name) == 0) { required_type = VK_LAYER_SETTING_TYPE_STRING_EXT; }
        else if (strcmp(VK_LAYER_DISABLES, name) == 0) { required_type = VK_LAYER_SETTING_TYPE_STRING_EXT; }''')

    for setting in all_settings:
        if IsDebugSetting(setting):
            continue
        if 'view' in setting and setting['view'] == 'HIDDEN':
            continue

        type = setting['type']
        layer_type = None
        if type == 'BOOL':
            layer_type = 'VK_LAYER_SETTING_TYPE_BOOL32_EXT'
        elif type == 'INT':
            layer_type = 'VK_LAYER_SETTING_TYPE_UINT32_EXT'
        elif type in ['SAVE_FILE', 'LIST', 'FLAGS']:
            layer_type = 'VK_LAYER_SETTING_TYPE_STRING_EXT'
        else:
            print(f'Warning, not type handled for {type} ({setting["key"]})')
            continue

        out.append(f'        else if (strcmp(VK_LAYER_{setting["key"].upper()}, name) == 0) {{ required_type = {layer_type}; }}')

    out.append('''        else {
            setting_warnings.emplace_back("The setting \\"" + std::string(name) +
                                          "\\" in VkLayerSettingsCreateInfoEXT was not recognized by the Validation Layers. Please "
                                          "view the VkLayer_khronos_validation.json for a list of all settings.");
        }

        if (required_type != VK_LAYER_SETTING_TYPE_MAX_ENUM_EXT && setting.type != required_type) {
            setting_warnings.emplace_back(
                "The setting \\"" + std::string(name) + "\\" in VkLayerSettingsCreateInfoEXT was set to type " +
                std::string(string_VkLayerSettingTypeEXT(setting.type)) + " but requires type " +
                std::string(string_VkLayerSettingTypeEXT(required_type)) + " and the value may be parsed incorrectly.");
        }

        if (used_settings.count(name)) {
            setting_warnings.emplace_back(
                "The setting \\"" + std::string(name) +
                "\\" in VkLayerSettingsCreateInfoEXT was listed twice and only the first one listed will be recognized.");
        }
        used_settings.insert(name);
    }
}
// clang-format on
''')

    text_output = common_ci.RepoRelative('layers/layer_options_validation.h')
    with open(text_output, 'w') as file:
        file.write('\n'.join(out))

if __name__ == '__main__':
    json_input = common_ci.RepoRelative('layers/VkLayer_khronos_validation.json.in')
    with open(json_input, 'r') as file:
        json_settings = json.load(file)['layer']['features']['settings']

    # Build up the list all at once into a flat list
    all_settings = []
    def Parse(setting):
        type = setting['type']
        if type == 'GROUP':
            for s in setting['settings']:
                Parse(s)
            return

        if type == 'FLAGS':
            for flags in setting['flags']:
                if 'settings' in flags:
                    for s in flags['settings']:
                        Parse(s)

        if 'settings' in setting:
            for s in setting['settings']:
                Parse(s)
        all_settings.append(setting)
    for setting in json_settings:
        Parse(setting)

    # Same on all OS (and easy to find setting in large generated list)
    all_settings = sorted(all_settings, key=lambda obj: obj['key'])

    GenerateTextFile(all_settings)
    GenerateValidation(all_settings)
